//#include "stdafx.h"
#include "../../BGAudioEngine/stdafx.h"
#include "AudioHostForASIO.h"
#include <stdlib.h>

///////////////////////////////////////////////////////
//	Contain pthread
#if defined(WIN32)
#include "pthread.h"
#else
#include <pthread.h>
#endif

pthread_mutex_t g_mutex;

extern AsioDrivers* asioDrivers;
static ASIODriverInfo driverInfo={0};
static bool postOutput=false;
static ASIOBufferInfo bufferInfos[64]; // buffer info's
static ASIOChannelInfo channelInfos[64]; // channel info's
static ASIOCallbacks asioCallbacks;
static long Buffer_Size=0;
static int nIcn=0;
static int nOcn=0;
static float* Left_Channel=NULL;
static float* Right_Channel=NULL;
static float* Temp_Left_Channel=NULL;
static float* Temp_Right_Channel=NULL;
static CAudioHostForASIO* g_obj=NULL;
static bool BE_SYS=false;//Current system is Big-Endian

//Check system byte-order is Big-Endian or Little-Endian
bool IsSystemBigEndian()
{
	union
	{
		int  iFourBytes;
		char  cOneByte;
	} uEndianChecker;

	uEndianChecker.iFourBytes = 1;
	if(uEndianChecker.cOneByte == 1)
		return false;
	else
		return true;
}

CAudioHostForASIO::CAudioHostForASIO(void)
	: m_nDevs(0)
	, m_fIsRun(false)
	, m_iCurDev(0)
	, m_pVSTArray(NULL)
	, m_nBufferSize(0)
	, m_iInChannels(2)
	, m_nVolume(100)
	, m_nVST(0)
	, MAX_BUFFER_SIZE(192000)
	, MIN_BUFFER_SIZE(128)
	, m_fVstChanged(false)
{
	BE_SYS=IsSystemBigEndian();

	memset(&m_wfx,0,sizeof(m_wfx));
	memset(m_pszDevNames,0,16*128);
	

	//Build up our driver lists
	if(!asioDrivers)
		asioDrivers = new AsioDrivers();

	//Get driver names
	char* p_dn[16]={0};
	for (int i=0;i<16;++i)
	{
		p_dn[i]=&m_pszDevNames[i][0];
	}

	m_nDevs=(int)(asioDrivers->getDriverNames(p_dn,16));
	
	SetCurrentListenDevice(m_iCurDev);

	pthread_mutex_init(&g_mutex,NULL);
}

CAudioHostForASIO::~CAudioHostForASIO(void)
{
	if(asioDrivers)
	{
		if(m_fIsRun)
		{
			Stop();
		}
		
		delete asioDrivers;
		asioDrivers=NULL;
	}
	memset(&driverInfo,0,sizeof(ASIODriverInfo));
	postOutput=false;
	pthread_mutex_destroy(&g_mutex);
}

bool CAudioHostForASIO::Init()
{
	bool rt=true;
	if(!asioDrivers->loadDriver(m_pszDevNames[m_iCurDev]))
		return false;
	if(ASIOInit(&driverInfo)!=ASE_OK)
	{
		asioDrivers->removeCurrentDriver();
		return false;
	}
	else
	{
		long inChnls=0,outChnls=0;
		long preferSize=0;
		long granularity=0;
		if(ASIOGetChannels(&inChnls,&outChnls)!=ASE_OK)
		{
			ASIOExit();
			asioDrivers->removeCurrentDriver();
			return false;
		}
		/*else
			if (outChnls!=2)
			{
				ASIOExit();
				asioDrivers->removeCurrentDriver();
				return false;
			}*/
		nOcn=(int)outChnls;

		m_iInChannels=(int)inChnls;
		if (ASIOGetBufferSize(&MIN_BUFFER_SIZE,&MAX_BUFFER_SIZE,&preferSize,&granularity)!=ASE_OK)
		{
			ASIOExit();
			asioDrivers->removeCurrentDriver();
			return false;
		}
		if (m_nBufferSize==0)
		{
			m_nBufferSize=preferSize;
		}
		ASIOSampleRate sr;
		if(ASIOGetSampleRate(&sr) != ASE_OK)
		{
			ASIOExit();
			asioDrivers->removeCurrentDriver();
			return false;
		}
		if (m_wfx.nSamplesPerSec!=0||sr <= 0.0 || sr > 96000.0)
		{
			sr=(m_wfx.nSamplesPerSec==0?44100.0:(double(m_wfx.nSamplesPerSec)));
			// Driver does not store it's internal sample rate, so set it to a know one.
			// Usually you should check beforehand, that the selected sample rate is valid
			// with ASIOCanSampleRate().
			if(ASIOSetSampleRate(44100.0) == ASE_OK)
			{
				if(ASIOGetSampleRate(&sr) != ASE_OK)
				{
					ASIOExit();
					asioDrivers->removeCurrentDriver();
					return false;
				}
			}
			else
			{
				ASIOExit();
				asioDrivers->removeCurrentDriver();
				return false;
			}
		}
		if(m_wfx.nSamplesPerSec==0)
		{
			m_wfx.nSamplesPerSec=(unsigned long)sr;
			m_wfx.nChannels=2;
			m_wfx.wBitsPerSample=16;
			m_wfx.nBlockAlign=m_wfx.nChannels*m_wfx.wBitsPerSample/8;
			m_wfx.nAvgBytesPerSec=m_wfx.nBlockAlign*m_wfx.nSamplesPerSec;
			m_wfx.cbSize=0;
		}
		if(ASIOOutputReady() == ASE_OK)
			postOutput = true;
		else
			postOutput = false;

	}
	return true;
}

void CAudioHostForASIO::Uninit()
{
	ASIOExit();
	asioDrivers->removeCurrentDriver();
}

int CAudioHostForASIO::GetListenDeviceCount(void)
{
	return m_nDevs;
}

int CAudioHostForASIO::GetCaptureDeviceCount(void)
{
	return m_nDevs;
}

char* CAudioHostForASIO::GetListenDeviceName(int i)
{
	if(i<0||i>=m_nDevs||m_nDevs==0)
		return NULL;
	return (char*)m_pszDevNames[i];
}

char* CAudioHostForASIO::GetCaptureDeviceName(int i)
{
	return GetListenDeviceName(i);
}

int CAudioHostForASIO::GetCurrentListenDevice(void)
{
	return m_iCurDev;
}

bool CAudioHostForASIO::SetCurrentListenDevice(int i)
{
	if(m_fIsRun)
		return false;
	if (i<0||i>=m_nDevs)
		return false;
	int t=m_iCurDev;
	m_iCurDev=i;
	if (Init())
	{
		Uninit();
		return true;
	}
	m_iCurDev=t;
	return false;
}

bool CAudioHostForASIO::SetCurrentListenDeviceByName(char* szDeviceName)
{
	for (int i=0;i<m_nDevs;++i)
	{
		if (strcmp(m_pszDevNames[i],szDeviceName)==0)
		{
			return SetCurrentListenDevice(i);
		}
	}
	return false;
}

int CAudioHostForASIO::GetCurrentCaptureDevice(void)
{
	return m_iCurDev;
}

bool CAudioHostForASIO::SetCurrentCaptureDevice(int i)
{
	return SetCurrentListenDevice(i);
}

bool CAudioHostForASIO::SetCurrentCaptureDeviceByName(char* szDeviceName)
{
	return SetCurrentListenDeviceByName(szDeviceName);
}

int CAudioHostForASIO::SetVolume(int Vol)//0-100
{
	if (Vol>100)
	{
		Vol=100;
	}
	if(Vol<0)
	{
		Vol=0;
	}
	pthread_mutex_lock(&g_mutex);
	m_nVolume=Vol;
	pthread_mutex_unlock(&g_mutex);
	return Vol;
}

int CAudioHostForASIO::GetVolume(void)
{
	return m_nVolume;
}

bool CAudioHostForASIO::SetFormat(WAVEFORMATEX* pwfx)
{
	if(m_fIsRun)
		return false;
	if (pwfx==NULL)
	{
		return false;
	}
	WAVEFORMATEX t;
	memcpy(&t,&m_wfx,sizeof(WAVEFORMATEX));
	memcpy(&m_wfx,pwfx,sizeof(WAVEFORMATEX));
	if (Init())
	{
		Uninit();
		return true;
	}
	memcpy(&m_wfx,pwfx,sizeof(WAVEFORMATEX));
	return false;
}

void CAudioHostForASIO::GetFormat(WAVEFORMATEX* pwfx)
{
	if(pwfx)
		memcpy(pwfx,&m_wfx,sizeof(WAVEFORMATEX));
}

//----------------------------------------------------------------------------------
// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
#if NATIVE_INT64
#define ASIO64toDouble(a)  (a)
#else
const double twoRaisedTo32 = 4294967296.;
#define ASIO64toDouble(a)  ((a).lo + (a).hi * twoRaisedTo32)
#endif

//Convert Big-Endian Integer to Little-Endian
inline void BE2LE(void* p,int nbytes)
{
	unsigned char t;
	int j=nbytes-1;
	int n=nbytes>>1;
	unsigned char* pc=(unsigned char*)p;
	for (int i=0;i<n;++i,--j)
	{
		t=pc[i];
		pc[i]=pc[j];
		pc[j]=t;
	}
}

//Convert Little-Endian Integer to Big-Endian
#define LE2BE(p,nbytes) BE2LE(p,nbytes)
#define LE_BE_COV(p,nbytes) BE2LE(p,nbytes)

inline void convert_samples(void* des,void* src,ASIOSampleType type,long buffSize,bool isInput)
{
	//LSB for little-endian while MSB for big-endian
	switch (type)
	{
	case ASIOSTInt16LSB:
	case ASIOSTInt16MSB:
		{
			const float rational=1.0/32767.0;
			short* pbs=isInput?((short*)src):((short*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if (
				isInput&&
				((BE_SYS&&type==ASIOSTInt16LSB)||
				(!BE_SYS&&type==ASIOSTInt16MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbs[i],2);
				}
			}
			for (long i=0;i<buffSize;++i)
			{
				if (isInput)
				{
					pbf[i]=(float)(pbs[i])*rational;
				}
				else
				{
					pbs[i]=(short)(pbf[i]*32767.0);
				}
			}
			if (
				!isInput&&
				((BE_SYS&&type==ASIOSTInt16LSB)||
				(!BE_SYS&&type==ASIOSTInt16MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbs[i],2);
				}
			}
		}
		break;
	case ASIOSTInt24LSB:		// used for 20 bits as well
	case ASIOSTInt24MSB:		// used for 20 bits as well
		{
			const unsigned long bit24_mask = 0xffffff00;
			const float rational=1.0/8388607.0;
			unsigned char* pbc=isInput?((unsigned char*)src):((unsigned char*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if (
				isInput&&
				((BE_SYS&&type==ASIOSTInt24LSB)||
				(!BE_SYS&&type==ASIOSTInt24MSB))
				)
			{
				for (long i=0;i<buffSize;i+=3)
				{
					LE_BE_COV((void*)&pbc[i],3);
				}
			}
			for (long i=0,j=0;i<buffSize;++i,j+=3)
			{
				char* tc=NULL;
				long tl=0;
				if (isInput)
				{
					tl=((long)((*((unsigned long*)(&pbc[j])))&bit24_mask))>>8;
					pbf[i]=((float)tl)*rational;
				}
				else
				{
					tl=((long)(pbf[i]*8388607.0))<<8;
					tc=(char*)&tl;
					pbc[j]=tc[0],pbc[j+1]=tc[1],pbc[j+2]=tc[2];
				}
			}
			if (
				!isInput&&
				((BE_SYS&&type==ASIOSTInt24LSB)||
				(!BE_SYS&&type==ASIOSTInt24MSB))
				)
			{
				for (long i=0;i<buffSize;i+=3)
				{
					LE_BE_COV((void*)&pbc[i],3);
				}
			}
		}
		break;
	case ASIOSTInt32LSB:
	case ASIOSTInt32MSB:
		{
			const float rational=1.0/2147483647.0;
			long* pbl=isInput?((long*)src):((long*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if (
				isInput&&
				((BE_SYS&&type==ASIOSTInt32LSB)||
				(!BE_SYS&&type==ASIOSTInt32MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbl[i],4);
				}
			}
			for (long i=0;i<buffSize;++i)
			{
				if (isInput)
				{
					pbf[i]=(float)(pbl[i])*rational;
				}
				else
				{
					pbl[i]=(long)(pbf[i]*2147483647.0);
				}
			}
			if (
				!isInput&&
				((BE_SYS&&type==ASIOSTInt32LSB)||
				(!BE_SYS&&type==ASIOSTInt32MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbl[i],4);
				}
			}
		}
		break;
	case ASIOSTFloat32LSB:		// IEEE 754 32 bit float, as found on Intel x86 architecture
	case ASIOSTFloat32MSB:		// IEEE 754 32 bit float, as found on Intel x86 architecture
		{
			float* pbf=isInput?((float*)src):((float*)des);
			if (
				isInput&&
				((BE_SYS&&type==ASIOSTFloat32LSB)||
				(!BE_SYS&&type==ASIOSTFloat32MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbf[i],4);
				}
			}
			memcpy (des, src, buffSize * sizeof(float));
			if (
				!isInput&&
				((BE_SYS&&type==ASIOSTFloat32LSB)||
				(!BE_SYS&&type==ASIOSTFloat32MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbf[i],4);
				}
			}
		}
		
		break;
	case ASIOSTFloat64LSB: 		// IEEE 754 64 bit double float, as found on Intel x86 architecture
	case ASIOSTFloat64MSB: 		// IEEE 754 64 bit double float, as found on Intel x86 architecture
		{
			double* pbd=isInput?((double*)src):((double*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if (
				isInput&&
				((BE_SYS&&type==ASIOSTFloat64LSB)||
				(!BE_SYS&&type==ASIOSTFloat64MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbd[i],8);
				}
			}
			for (long i=0;i<buffSize;++i)
			{
				if (isInput)
				{
					pbf[i]=(float)pbd[i];
				}
				else
				{
					pbd[i]=(double)pbf[i];
				}
			}
			if (
				!isInput&&
				((BE_SYS&&type==ASIOSTFloat64LSB)||
				(!BE_SYS&&type==ASIOSTFloat64MSB))
				)
			{
				for (long i=0;i<buffSize;++i)
				{
					LE_BE_COV((void*)&pbd[i],8);
				}
			}
		}
		break;

		// these are used for 32 bit data buffer, with different alignment of the data inside
		// 32 bit PCI bus systems can more easily used with these
	case ASIOSTInt32LSB16:		// 32 bit data with 18 bit alignment
	case ASIOSTInt32MSB16:		// 32 bit data with 18 bit alignment
		{
			const float rational=1.0/2147483647.0;
			unsigned long* pbul=isInput?((unsigned long*)src):((unsigned long*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if(BE_SYS)
			{
				if (isInput)
				{
					if (type==ASIOSTInt32LSB16)
					{
						for (long i=0;i<buffSize;++i)
						{
							LE2BE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{	
							pbul[i]<<=16;
						}
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{
						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (!isInput)
				{
					if (type==ASIOSTInt32LSB16)
					{
						for (long i=0;i<buffSize;++i)
						{
							BE2LE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{
							pbul[i]>>=16;
						}
					}
				}
			}
			else
			{
				if (type==ASIOSTInt32MSB16&&isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						BE2LE((void*)&pbul[i],4);
						pbul[i]<<=16;
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{
							
						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (type==ASIOSTInt32MSB16&&!isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						pbul[i]>>=16;
						LE2BE((void*)&pbul[i],4);
					}
				}
			}
		}
		break;

	case ASIOSTInt32LSB18:		// 32 bit data with 18 bit alignment
	case ASIOSTInt32MSB18:		// 32 bit data with 18 bit alignment
		{
			const float rational=1.0/2147483647.0;
			unsigned long* pbul=isInput?((unsigned long*)src):((unsigned long*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if(BE_SYS)
			{
				if (isInput)
				{
					if (type==ASIOSTInt32LSB18)
					{
						for (long i=0;i<buffSize;++i)
						{
							LE2BE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{	
							pbul[i]<<=14;
						}
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{
						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (!isInput)
				{
					if (type==ASIOSTInt32LSB18)
					{
						for (long i=0;i<buffSize;++i)
						{
							BE2LE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{
							pbul[i]>>=14;
						}
					}
				}
			}
			else
			{
				if (type==ASIOSTInt32MSB18&&isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						BE2LE((void*)&pbul[i],4);
						pbul[i]<<=14;
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{

						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (type==ASIOSTInt32MSB18&&!isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						pbul[i]>>=14;
						LE2BE((void*)&pbul[i],4);
					}
				}
			}
		}
		break;

	case ASIOSTInt32LSB20:		// 32 bit data with 20 bit alignment
	case ASIOSTInt32MSB20:		// 32 bit data with 20 bit alignment
		{
			const float rational=1.0/2147483647.0;
			unsigned long* pbul=isInput?((unsigned long*)src):((unsigned long*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if(BE_SYS)
			{
				if (isInput)
				{
					if (type==ASIOSTInt32LSB20)
					{
						for (long i=0;i<buffSize;++i)
						{
							LE2BE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{	
							pbul[i]<<=12;
						}
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{
						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (!isInput)
				{
					if (type==ASIOSTInt32LSB20)
					{
						for (long i=0;i<buffSize;++i)
						{
							BE2LE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{
							pbul[i]>>=12;
						}
					}
				}
			}
			else
			{
				if (type==ASIOSTInt32MSB20&&isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						BE2LE((void*)&pbul[i],4);
						pbul[i]<<=12;
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{

						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (type==ASIOSTInt32MSB20&&!isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						pbul[i]>>=12;
						LE2BE((void*)&pbul[i],4);
					}
				}
			}
		}
		break;

	case ASIOSTInt32LSB24:		// 32 bit data with 24 bit alignment
	case ASIOSTInt32MSB24:		// 32 bit data with 24 bit alignment
		{
			const float rational=1.0/2147483647.0;
			unsigned long* pbul=isInput?((unsigned long*)src):((unsigned long*)des);
			float* pbf=isInput?((float*)des):((float*)src);
			if(BE_SYS)
			{
				if (isInput)
				{
					if (type==ASIOSTInt32LSB24)
					{
						for (long i=0;i<buffSize;++i)
						{
							LE2BE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{	
							pbul[i]<<=8;
						}
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{
						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (!isInput)
				{
					if (type==ASIOSTInt32LSB24)
					{
						for (long i=0;i<buffSize;++i)
						{
							BE2LE((void*)&pbul[i],4);
						}
					}
					else
					{
						for (long i=0;i<buffSize;++i)
						{
							pbul[i]>>=8;
						}
					}
				}
			}
			else
			{
				if (type==ASIOSTInt32MSB24&&isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						BE2LE((void*)&pbul[i],4);
						pbul[i]<<=8;
					}
				}
				for (long i=0;i<buffSize;++i)
				{
					if (isInput)
					{

						pbf[i]=(float)((long)pbul[i])*rational;
					}
					else
					{
						pbul[i]=(unsigned long)((long)(pbf[i]*2147483647.0));
					}
				}
				if (type==ASIOSTInt32MSB24&&!isInput)
				{
					for (long i=0;i<buffSize;++i)
					{
						pbul[i]>>=8;
						LE2BE((void*)&pbul[i],4);
					}
				}
			}
		}
		break;
	}
}

ASIOTime* CAudioHostForASIO::bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
{	// the actual processing callback.
	// Beware that this is normally in a seperate thread, hence be sure that you take care
	// about thread synchronization. This is omitted here for simplicity.

	// buffer size in samples
	long buffSize = Buffer_Size;
	float* channel_buffer_array[2];
	channel_buffer_array[0]=Left_Channel,channel_buffer_array[1]=Right_Channel;

	// perform the processing
	memset(Left_Channel,0,sizeof(float)*buffSize);
	memset(Right_Channel,0,sizeof(float)*buffSize);
	if (nIcn%2!=0)//odd accumulation
	{
		for (int i=0;i<nIcn+nOcn;++i)
		{
			if (bufferInfos[i].isInput==ASIOTrue)
			{
				convert_samples((void*)Right_Channel,bufferInfos[i].buffers[index],channelInfos[i].type,buffSize,true);
				for (long j=0;j<buffSize;++j)
				{
					Left_Channel[j]+=Right_Channel[j];
				}
			}
		}
		for (long j=0;j<buffSize;++j)
		{
			Right_Channel[j]=Left_Channel[j];
		}
	}
	else//even accumulation
	{
		for (int i=0;i<nIcn+nOcn;++i)
		{
			if (bufferInfos[i].isInput==ASIOTrue)
			{
				convert_samples((void*)Temp_Left_Channel,bufferInfos[i].buffers[index],channelInfos[i].type,buffSize,true);
				++i;
				convert_samples((void*)Temp_Right_Channel,bufferInfos[i].buffers[index],channelInfos[i].type,buffSize,true);
				for (long j=0;j<buffSize;++j)
				{
					Left_Channel[j]+=Temp_Left_Channel[j];
					Right_Channel[j]+=Temp_Right_Channel[j];
				}
			}
		}
	}

	pthread_mutex_lock(&g_mutex);
	//process
	float vol=((float)g_obj->m_nVolume)*0.01;
	for (long i=0;i<buffSize;i++)
	{
		Left_Channel[i]*=vol;
		Right_Channel[i]*=vol;
	}
	if(g_obj->m_fVstChanged)
	{
		if(g_obj->m_pVSTArray){
			for(int i=0;i<g_obj->m_nVST;++i)
			{
				g_obj->m_pVSTArray[i]->SetBuffer(g_obj->m_wfx.nSamplesPerSec,buffSize);
			}
		}
		g_obj->m_fVstChanged=false;
	}
	if(g_obj->m_pVSTArray){
		for(int i=0;i<g_obj->m_nVST;++i)
		{
			g_obj->m_pVSTArray[i]->Process(Left_Channel,Right_Channel);
		}
	}
	pthread_mutex_unlock(&g_mutex);

	g_obj->HandleSamplesRaw(Left_Channel,Right_Channel,(unsigned long)buffSize);

	//Send to Output
	int k=0,d,id[2]={0};;
	for (int i=0;i<nIcn+nOcn;++i)
	{
		if (bufferInfos[i].isInput==ASIOFalse)
		{
			if(k<2)
			{
				convert_samples(bufferInfos[i].buffers[index],(void*)channel_buffer_array[k],channelInfos[i].type,buffSize,false);
				id[k]=i;
			}
			else
			{
				switch(channelInfos[i].type)
				{
				case ASIOSTInt16MSB:
				case ASIOSTInt16LSB:
					d=2;
					break;
				case ASIOSTInt24MSB:
				case ASIOSTInt24LSB:
					d=3;
					break;
				case ASIOSTInt32MSB:
				case ASIOSTInt32LSB:
					d=4;
					break;
				case ASIOSTFloat32MSB:
				case ASIOSTFloat32LSB:
					d=4;
					break;
				case ASIOSTFloat64MSB:
				case ASIOSTFloat64LSB:
					d=8;
					break;
				case ASIOSTInt32MSB16:
				case ASIOSTInt32MSB18:
				case ASIOSTInt32MSB20:
				case ASIOSTInt32MSB24:
				case ASIOSTInt32LSB16:
				case ASIOSTInt32LSB18:
				case ASIOSTInt32LSB20:
				case ASIOSTInt32LSB24:
					d=4;
					break;
				}
				if (k==(nOcn-1)&&(k&1)&&(nIcn&1))
				{
					for (long j=0;j<buffSize;++j)
					{
						Left_Channel[j]+=Right_Channel[j];
					}
					convert_samples(bufferInfos[i].buffers[index],Left_Channel,channelInfos[i].type,buffSize,false);
				}
				else
				{
					memcpy(bufferInfos[i].buffers[index],bufferInfos[id[k&1]].buffers[index],buffSize*d);
				}
			}
			++k;
		}
	}

	// finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
	if (postOutput)
		ASIOOutputReady();

	return 0L;
}

//----------------------------------------------------------------------------------
void CAudioHostForASIO::bufferSwitch(long index, ASIOBool processNow)
{	// the actual processing callback.
	// Beware that this is normally in a seperate thread, hence be sure that you take care
	// about thread synchronization. This is omitted here for simplicity.

	// as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
	// though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
	ASIOTime  timeInfo;
	memset (&timeInfo, 0, sizeof (timeInfo));

	//// get the time stamp of the buffer, not necessary if no
	//// synchronization to other media is required
	//if(ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
	//	timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;

	bufferSwitchTimeInfo (NULL, index, processNow);
}


//----------------------------------------------------------------------------------
void CAudioHostForASIO::sampleRateChanged(ASIOSampleRate sRate)
{
	// do whatever you need to do if the sample rate changed
	// usually this only happens during external sync.
	// Audio processing is not stopped by the driver, actual sample rate
	// might not have even changed, maybe only the sample rate status of an
	// AES/EBU or S/PDIF digital input at the audio device.
	// You might have to update time/sample related conversion routines, etc.
}

//----------------------------------------------------------------------------------
long CAudioHostForASIO::asioMessages(long selector, long value, void* message, double* opt)
{
	// currently the parameters "value", "message" and "opt" are not used.
	long ret = 0;
	switch(selector)
	{
	case kAsioSelectorSupported:
		if(value == kAsioResetRequest
			|| value == kAsioEngineVersion
			|| value == kAsioResyncRequest
			|| value == kAsioLatenciesChanged
			// the following three were added for ASIO 2.0, you don't necessarily have to support them
			|| value == kAsioSupportsTimeInfo
			|| value == kAsioSupportsTimeCode
			|| value == kAsioSupportsInputMonitor)
			ret = 1L;
		break;
	case kAsioResetRequest:
		// defer the task and perform the reset of the driver during the next "safe" situation
		// You cannot reset the driver right now, as this code is called from the driver.
		// Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
		// Afterwards you initialize the driver again.
		// asioDriverInfo.stopped;  // In this sample the processing will just stop
		ret = 1L;
		break;
	case kAsioResyncRequest:
		// This informs the application, that the driver encountered some non fatal data loss.
		// It is used for synchronization purposes of different media.
		// Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
		// Windows Multimedia system, which could loose data because the Mutex was hold too long
		// by another thread.
		// However a driver can issue it in other situations, too.
		ret = 1L;
		break;
	case kAsioLatenciesChanged:
		// This will inform the host application that the drivers were latencies changed.
		// Beware, it this does not mean that the buffer sizes have changed!
		// You might need to update internal delay data.
		ret = 1L;
		break;
	case kAsioEngineVersion:
		// return the supported ASIO version of the host application
		// If a host applications does not implement this selector, ASIO 1.0 is assumed
		// by the driver
		ret = 2L;
		break;
	case kAsioSupportsTimeInfo:
		// informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
		// is supported.
		// For compatibility with ASIO 1.0 drivers the host application should always support
		// the "old" bufferSwitch method, too.
		ret = 1;
		break;
	case kAsioSupportsTimeCode:
		// informs the driver wether application is interested in time code info.
		// If an application does not need to know about time code, the driver has less work
		// to do.
		ret = 0;
		break;
	}
	return ret;
}

bool CAudioHostForASIO::CreateBuffer()
{
	asioCallbacks.bufferSwitch = &bufferSwitch;
	asioCallbacks.sampleRateDidChange = &sampleRateChanged;
	asioCallbacks.asioMessage = &asioMessages;
	asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
	long i;
	ASIOError result;
	ASIOBufferInfo *info = bufferInfos;

	// prepare inputs
	for(int i = 0; i < m_iInChannels; ++i, ++info)
	{
		info->isInput = ASIOTrue;
		info->channelNum = i;
		info->buffers[0] = info->buffers[1] = 0;
	}

	// prepare outputs
	for(int i = 0; i < nOcn; ++i, ++info)
	{
		info->isInput = ASIOFalse;
		info->channelNum = i;
		info->buffers[0] = info->buffers[1] = 0;
	}

	// create and activate buffers
	result = ASIOCreateBuffers(bufferInfos,
		m_iInChannels+nOcn,
		m_nBufferSize, &asioCallbacks);
	if (result == ASE_OK)
	{
		// now get all the buffer details, sample word length, name, word clock group and activation
		for (i = 0; i < m_iInChannels + nOcn; i++)
		{
			channelInfos[i].channel = bufferInfos[i].channelNum;
			channelInfos[i].isInput = bufferInfos[i].isInput;
			result = ASIOGetChannelInfo(&channelInfos[i]);
			if (result != ASE_OK)
				break;
		}
	}
	if(result!=ASE_OK)
		return false;
	Buffer_Size=m_nBufferSize;
	nIcn=m_iInChannels;
	Left_Channel=new float[m_nBufferSize];
	Right_Channel=new float[m_nBufferSize];
	Temp_Left_Channel=new float[m_nBufferSize];
	Temp_Right_Channel=new float[m_nBufferSize];
	return true;
}

void CAudioHostForASIO::DeleteBuffer()
{
	ASIODisposeBuffers();
	if (Left_Channel)
	{
		delete[] Left_Channel;
		Left_Channel=NULL;
	}
	if (Right_Channel)
	{
		delete[] Right_Channel;
		Right_Channel=NULL;
	}
	if (Temp_Left_Channel)
	{
		delete[] Temp_Left_Channel;
		Temp_Left_Channel=NULL;
	}
	if (Temp_Right_Channel)
	{
		delete[] Temp_Right_Channel;
		Temp_Right_Channel=NULL;
	}
}

bool CAudioHostForASIO::Start(void)
{
	if (m_fIsRun)
	{
		return false;
	}
	if (Init())
	{
		if(CreateBuffer())
		{
			g_obj=this;
			if (ASIOStart()==ASE_OK)
			{
				if(CAudioHost::pMixer)
				{
					CAudioHost::SetSampleRate(m_wfx.nSamplesPerSec);
					CAudioHost::SetBufferSize(m_nBufferSize);
				}
				m_fIsRun=true;
				return true;
			}
			else
			{
				DeleteBuffer();
				Uninit();
			}
		}
	}
	return false;
}

void CAudioHostForASIO::Stop(void)
{
	if (!m_fIsRun)
	{
		return;
	}
	ASIOStop();
	g_obj=NULL;
	DeleteBuffer();
	Uninit();
	m_fIsRun=false;
}

bool CAudioHostForASIO::SetVstEffect(CWavVstHost** pVSTArray,int nCount)
{
	if(nCount<0)
	{
		return false;
	}
	pthread_mutex_lock(&g_mutex);
	if(m_pVSTArray)
	{
		free(m_pVSTArray);
		m_pVSTArray=NULL;
		m_nVST=0;
	}
	if(nCount>0&&pVSTArray!=NULL)
	{
		m_pVSTArray=(CWavVstHost**)malloc(sizeof(CWavVstHost*)*nCount);
		m_nVST=nCount;
		for(int i=0;i<nCount;++i)
		{
			m_pVSTArray[i]=pVSTArray[i];
		}
	}
	m_fVstChanged=true;
	pthread_mutex_unlock(&g_mutex);
	return true;
}

bool CAudioHostForASIO::SetBufferSize(int size)
{
	if(m_fIsRun)
		return false;
	if(size>=(MIN_BUFFER_SIZE*m_wfx.nBlockAlign)&&size<=(MAX_BUFFER_SIZE*m_wfx.nBlockAlign))
	{
		m_nBufferSize=size/m_wfx.nBlockAlign;
		CAudioHost::SetBufferSize(m_nBufferSize);
		return true;
	}
	return false;
}